package com.tbit.uqbike.client.controller;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tbit.uqbike.client.common.ResultMsg;
import com.tbit.uqbike.client.constant.*;
import com.tbit.uqbike.client.exception.BaseException;
import com.tbit.uqbike.client.pojo.*;
import com.tbit.uqbike.client.pojo.newEntity.AccountConfigExt;
import com.tbit.uqbike.client.pojo.newEntity.ReturnBikeLog;
import com.tbit.uqbike.client.service.*;
import com.tbit.uqbike.client.util.Assert;
import com.tbit.uqbike.client.util.DateTimeUtil;
import com.tbit.uqbike.client.util.LogUtil;
import com.tbit.uqbike.client.util.OutputUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.*;

/**
 * 区域调度判断
 *
 * @author Leon
 * 2019年10月16日 下午5:07:39
 */
@RestController
@RequestMapping("/dispatch")
@Slf4j
public class DispatchController {
    @Autowired
    private TokenService tokenService;
    @Autowired
    private MachineBorrowService machineBorrowService;
    @Autowired
    private CalibrateService calibrateService;
    @Autowired
    private DispatchConfigService dispatchConfigService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private OssPushLogService ossPushLogService;
    @Autowired
    private MachineService machineService;
    @Autowired
    private TerControlService terControlService;

    /**
     * 获取终端最新状态指令
     *
     * @return
     */
    @GetMapping("/getNewState")
    public Result getNewState(String token, String userCode, String machineNO) {
        User user = tokenService.getUser(token);
        Machine machine;
        if (StringUtils.isNotEmpty(userCode)) {
            userCode = userCode.trim();
            machine = machineService.getByUserCode(userCode);
        } else {
            Assert.hasLength(machineNO, "-30001", "machineNO不能为空");
            machineNO = machineNO.trim();
            machine = machineService.getByMachineNO(machineNO);
        }
        if (machine == null) {
            throw new BaseException("-30005", "设备不存在");
        }
        terControlService.sendControl(UUID.randomUUID().toString(), machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_GET,
                ControlTypeConstant.CONTROL_TYPE_DEVICESTATUS + ControlTypeConstant.CONTROL_TYPE_DEVICESTATUSEX, "", machine, false, -1, user.getUserId(), "预临停信息获取");
        return Result.success();
    }

    /**
     * 手机上传融合定位包数据
     *
     * @param token    令牌
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/phonefuselocation")
    public void phonefuselocation(String token, String data,
                                  HttpServletRequest request, HttpServletResponse response) {
//		logger.info("phonefuselocation token="+token);
        LogUtil.info("接收到手机融合定位包数据：" + data);
        Assert.notNull(token, "-30006", "token不能为空");
//		Assert.notNull(data,"-30006","data不能为空");
        Map<String, Object> map = new HashMap<String, Object>();
        User user = tokenService.getUser(token);
        Machine machine = Optional.ofNullable(user)
                .map(o -> machineBorrowService.getByUserId(o.getUserId()))
                .map(o -> o.getMachineId())
                .map(o -> machineService.getByMachineId(o))
                .orElseGet(() -> null);
        /**如果为空说明已经还车，不在接收数据，不为空时才接收数据**/
        if (null != machine) {
            String mahcineNO = machine.getMachineNO();
            LogUtil.info("mahcineNO：" + mahcineNO);
            /**数据有效性（必须包含这辆车数据，本车辆数据不存，否则无效）**/
            boolean validity = false;
            List<PhoneFuselocationData> nodes = JSON.parseArray(data, PhoneFuselocationData.class);
            Set bleMacSet = new HashSet();
            List<PhoneFuselocationData> rhdwbs = new ArrayList<PhoneFuselocationData>();
            Date date = new Date();
            for (PhoneFuselocationData bn : nodes) {
                if (Objects.equals(mahcineNO, bn.getMachineNO())) {
                    validity = true;
                } else {
                    bn.setUpdateTime(date);
                    rhdwbs.add(bn);
                    bleMacSet.add(bn.getMachineNO());
                }
            }
            if (validity && CollectionUtil.isNotEmpty(rhdwbs)) {
                /**1.取出老的融合定位包数据**/
                String rediskey = RedisConstant.PHONE_FUSELOCATION_DATA + mahcineNO;
                List<PhoneFuselocationData> oldList = Optional.ofNullable(redisService.get(rediskey))
                        .map(o -> JSONObject.parseArray(o, PhoneFuselocationData.class))
                        .orElseGet(() -> null);
//				logger.info("手机融合定位包老数据：mahcineNO["+mahcineNO+"]json:["+JSON.toJSONString(oldList)+"]");
                if (CollectionUtil.isNotEmpty(oldList)) {
                    for (PhoneFuselocationData o : oldList) {
                        /**20s内且机器号不重复的数据重新放回redis内**/
                        if (DateTimeUtil.isInTime(o.getUpdateTime(), date, 20l) && !bleMacSet.contains(o.getMachineNO())) {
                            rhdwbs.add(o);
                        }
                    }
                }
                /**2.将新的融合定位包数据存入redis**/
                if (CollectionUtil.isNotEmpty(rhdwbs)) {
//					logger.info("手机融合定位包新数据：mahcineNO["+mahcineNO+"]json:["+JSON.toJSONString(rhdwbs)+"]");
                    redisService.add(rediskey, JSON.toJSONString(rhdwbs), 20);
                }
            }
        }
        map.put(ResponseCode.RET, ResponseCode.SUCC);
        map.put(ResponseCode.DATA, null);
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 调度检查
     *
     * @param token           令牌
     * @param mapType         地图类型(默认谷歌)
     * @param lo              经度
     * @param la              纬度
     * @param isLast          最后轮询次数
     * @param force           强制还车标识 1代表强制还车
     * @param combineDispatch 新版组合收取调度费标识 1代表超区/站外+头盔锁组合调度，不传代表旧版只收取超区站外调度费
     * @param isFrame        是否弹出无定位弹窗 0代表是，1代表否
     * @ignoreParams request
     */
    @RequestMapping("/check")
    public Result<Dispatch> check(String token, Double lo, Double la,@RequestParam(required = false, defaultValue = "-1")String phoneTimeStamp,
                                  @RequestParam(required = false, defaultValue = "1.0") Double accuracy,
                                  @RequestParam(required = false, defaultValue = "2") Integer mapType,
                                  @RequestParam(defaultValue = "0") Integer isLast,
                                  @RequestParam(defaultValue = "0") Integer force,
                                  @RequestParam(defaultValue = "0") Integer combineDispatch,
                                  @RequestParam(defaultValue = "0") Integer isFrame) {
        long timeStamp = 0L;
        // 没传即旧版小程序情况
        if (Objects.equals("-1",phoneTimeStamp)){
            timeStamp = System.currentTimeMillis();
        } else if (Objects.equals("undefined",phoneTimeStamp)) {
            timeStamp = 0L;
            log.error("还车调度检查，小程序传入的手机定位时间 undefined");
        }else {
            timeStamp = Long.parseLong(phoneTimeStamp);
        }


        User user = tokenService.getUser(token);
        MachineBorrow machineBorrow = machineBorrowService.getByUserId(user.getUserId());
        if (machineBorrow == null) {
            throw new BaseException("-3006", "已经还车，不存在借出记录");
        }
        //不支持调度，不在还车点内
        Dispatch dispatch;
        Map<String, Object> rideMap = SystemData.ridelogMap.getOrDefault(machineBorrow.getOrderNO(), new HashMap<>());
        Point point = null;
        //如果传了经纬度
        if (lo != null && la != null) {
            point = calibrateService.calibrate(new Point(lo, la), mapType, SysConstant.ORIGINAL);
            rideMap.put("phoneLon", point.getX());
            rideMap.put("phoneLat", point.getY());
        }
        rideMap.put("orderNO", machineBorrow.getOrderNO());
        rideMap.put("accountId", user.getAccountId());


        dispatch = dispatchConfigService.dispatchCheck(machineBorrow.getMachineId(), point, rideMap, force, combineDispatch, isLast, timeStamp,accuracy,isFrame);
        //兼容站外超区豁免次数
        int adAccountId = 0;
        if (rideMap.get("adAccountId") != null) {
            adAccountId = (int) rideMap.get("adAccountId");
        }

        dispatchConfigService.photoOrQrCheck(dispatch, adAccountId, machineBorrow.getOrderNO(), isLast);
        dispatchConfigService.discountCheck(dispatch, adAccountId, user, machineBorrow.getOrderNO(), rideMap.get("helmetSwitch"));

        addLog(rideMap, dispatch, machineBorrow, isLast, user,isFrame);

        return Result.success(dispatch);
    }

    /**
     * 新版本调度检查
     *
     * @param token    令牌
     * @param mapType  地图类型
     * @param lo       经度
     * @param la       纬度
     * @param isLast   是否最后轮询
     * @param response 响应
     * @ignoreParams request
     * @deprecated 没用上，不再使用
     */
    @Deprecated
    @RequestMapping("/checkNew")
    public void checkNew(String token, Integer mapType, Double lo, Double la, @RequestParam(defaultValue = "0") Integer isLast,
                         HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            User user = tokenService.getUser(token);

            /**地图类型为空，默认谷歌*/
            if (mapType == null) {
                mapType = SysConstant.GOOGLE;
            }

            MachineBorrow machineBorrow = machineBorrowService.getByUserId(user.getUserId());
            if (machineBorrow == null) {
                throw new BaseException("-3006", "已经还车，不存在借出记录");
            }
            //不支持调度，不在还车点内
            Dispatch dispatch = new Dispatch(2, null);

            Map<String, Object> rideMap = SystemData.ridelogMap.get(machineBorrow.getOrderNO());
            if (rideMap == null) {
                rideMap = new HashMap<>();
            }
            Point point = null;
            /**如果传了经纬度*/
            if (lo != null && la != null) {
                point = calibrateService.calibrate(new Point(lo, la), mapType, SysConstant.ORIGINAL);
                rideMap.put("phoneLon", point.getX());
                rideMap.put("phoneLat", point.getY());
            }
            rideMap.put("orderNO", machineBorrow.getOrderNO());
            rideMap.put("accountId", user.getAccountId());


            dispatch = dispatchConfigService.dispatchCheckNew(machineBorrow.getMachineId(), point, rideMap, isLast);

            addLog(rideMap, dispatch, machineBorrow, isLast, user, 0);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
            map.put(ResponseCode.DATA, dispatch);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 添加日志
     *
     * @param rideMap
     * @param dispatch
     * @param machineBorrow
     * @param isLast
     * @param user
     */
    public void addLog(Map<String, Object> rideMap, Dispatch dispatch, MachineBorrow machineBorrow, Integer isLast,
                       User user,Integer isFrame) {
        rideMap.put("result", dispatch.getType());
        Long order = redisService.getAtomicLong(RedisConstant.RETURN_NUM + machineBorrow.getOrderNO());
        if (Objects.isNull(order)) {
            order = 0L;
        }


        LogUtil.info(machineBorrow.getOrderNO() + "还车结果=" + dispatch + ",isLast=" + isLast+",isFrame="+isFrame);
        rideMap.put("num", order.toString());
        /**isLast标识是否是最后一次轮询*/
        if (isLast == 1 || (DisPatchConstant.DISPATCH_PARK.equals(dispatch.getType()) &&
                (Objects.isNull(dispatch.getSecondType()) || DisPatchConstant.DISPATCH_PARK.equals(dispatch.getSecondType())) ) ) {
            ReturnBikeLog returnBikeLog = new ReturnBikeLog();
            returnBikeLog.setOrderNO(machineBorrow.getOrderNO());
            returnBikeLog.setUserCode(rideMap.get("userCode").toString());
            returnBikeLog.setMachineNO(rideMap.get("machineNO").toString());
            returnBikeLog.setParkPointId(rideMap.get("parkPointId") == null ? null : (int) rideMap.get("parkPointId"));
            returnBikeLog.setReturnCode(dispatch.getType());
            returnBikeLog.setAddTime(new Date());
            returnBikeLog.setUserId(user.getUserId());
            SystemData.logList.add(returnBikeLog);
            AccountConfigExt accountConfigExt = cacheService.getAccountConfigExtByKey((int) rideMap.get("adAccountId"),
                    AccountConfigExtConstant.ACCOUNTCONFIG_PUSHLOG);
            //是否是两种组合调度类型
            boolean flag = dispatch.getSecondType() != null && !Objects.equals(dispatch.getType(), dispatch.getSecondType());
            if (accountConfigExt != null && accountConfigExt.getParamValue().equals("1")) {
                terControlService.send(RabbitMQData.queueExchange, RabbitMQData.slskey, String.valueOf(GatewayProtocol.MQ_MSG_ID_SLS_LOG_RETURN_PUSH), "", JSON.toJSONString(rideMap));
                if (flag) {
                    rideMap.put("result", dispatch.getSecondType());
                    terControlService.send(RabbitMQData.queueExchange, RabbitMQData.slskey, String.valueOf(GatewayProtocol.MQ_MSG_ID_SLS_LOG_RETURN_PUSH), "", JSON.toJSONString(rideMap));
                }
            }
            if (flag) {
                returnBikeLog.setReturnCode(dispatch.getSecondType());
                SystemData.logList.add(returnBikeLog);
            }
        }

        // 放入redis
        if (dispatch.getType().equals(DisPatchConstant.DISPATCH_PARK)
                || dispatch.getType().equals(DisPatchConstant.DISPATCH_NOT_IN_AREA)
                || dispatch.getType().equals(DisPatchConstant.DISPATCH_NO_IN_PARK)
                || dispatch.getType().equals(DisPatchConstant.DISPATCH_OVERFLOW_PARK)) {
            /**记录还车条件判断标识*/
            redisService.add(RedisConstant.USER_RETURN_BIKE + machineBorrow.getUserId(), "1", 30);
        }

    }

    /**
     * 还车条件预先指令下发
     *
     * @param userCode 车辆编号
     * @param response 响应
     * @ignoreParams request
     */
    @RequestMapping("/returnCheck")
    public void returnCheck(String userCode, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Assert.notNull(userCode, "-30006", "车辆编号不能为空");
            dispatchConfigService.returnCheck(userCode);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 拍照还车
     *
     * @param multipartFile
     * @param token
     * @param request
     * @param response
     */
    @RequestMapping("/photoUpload")
    public void photoUpload(MultipartFile multipartFile, String token, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();
        try {
            if (multipartFile != null && multipartFile.isEmpty()) {
                throw new BaseException("-4004", "请上传图片");
            }
            User user = tokenService.getUser(token);
            dispatchConfigService.photoUpload(multipartFile, user);
            map.put(ResponseCode.RET, ResponseCode.SUCC);
        } catch (BaseException e) {
            ResponseCode.printException(map, e, request);
        }
        OutputUtil.renderText(request, response, map);
    }

    /**
     * 车牌拍照还车
     *
     * @param token
     * @param orderNO 骑行订单编号
     * @param file    图片文件(单张)
     * @Return Boolean 是否成功.true:成功,false:失败
     */
    @PostMapping("/returnByPhotoCheck")
    public ResultMsg<Boolean> returnByPhotoCheck(@RequestParam(value = "filePath", required = true) MultipartFile file,
                                                 @RequestParam @NotBlank String orderNO, @NotBlank String token,Double lon,Double lat,
                                                 @RequestParam(defaultValue = "2") Integer mapType) {
        User user = tokenService.getUser(token);
        Assert.notNull(user, "-20005", "用户不存在");
        Point point = new Point(lon,lat);
        if (lon != null && lat != null) {
            point = calibrateService.calibrate(new Point(lon, lat), mapType, SysConstant.ORIGINAL);
        }
        return ResultMsg.success(dispatchConfigService.returnByPhotoCheck(user, orderNO, file,point));
    }

    /**
     * 二维码还车
     *
     * @param token
     * @param orderNO       骑行订单编号
     * @param scanMachineNo 扫码机器编号
     * @Return Boolean 是否成功.true:成功,false:失败
     */
    @RequestMapping("/returnByQrCheck")
    public ResultMsg<Boolean> returnByQrCheck(@NotBlank String token, @NotBlank String orderNO, @NotBlank String scanMachineNo,Double lon,Double lat
    ,@RequestParam(defaultValue = "2") Integer mapType) {
        User user = tokenService.getUser(token);
        Assert.notNull(user, "-20005", "用户不存在");
        Point point = new Point(lon,lat);
        if (lon != null && lat != null) {
            point = calibrateService.calibrate(new Point(lon, lat), mapType, SysConstant.ORIGINAL);
        }
        return ResultMsg.success(dispatchConfigService.returnByQrCheck(user, orderNO, scanMachineNo,point));
    }


    /**
     * @description: 学习规范免调度费，添加调度费到缓存
     * @author: mo.shanyong
     * @date: 2025/2/28 14:30
     * @param: orderNO 订单号
     * @param: reduceMoney 调度金额
     * @return: com.tbit.uqbike.client.common.ResultMsg<java.lang.Object>
     */
    @PostMapping("/addReduceDispatchMoney")
    public ResultMsg<Object> addReduceDispatchMoney(String orderNO,Integer reduceMoney){
        dispatchConfigService.addReduceDispatchMoney(orderNO, reduceMoney);
        return ResultMsg.success();
    }
}
